package com.ljx.funchat.common.service.lock;

import com.ljx.funchat.common.exception.BusinessException;
import com.ljx.funchat.common.exception.CommonErrorEnum;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class LockService {

    @Autowired
    private RedissonClient redissonClient;

    /**
     *
     * @param key
     * @param waiteTime
     * @param timeUnit
     * @param supplier 只有出参没有入参, Function：既有出参又有入参
     * @param <T>
     * @return
     */
    @SneakyThrows
    public <T> T executeWithLock(String key, int waiteTime, TimeUnit timeUnit, Supplier<T> supplier) {
        RLock lock = redissonClient.getLock(key);
        boolean b = lock.tryLock(waiteTime, timeUnit);
        if (!b){
            throw new BusinessException(CommonErrorEnum.LOCK_LIMIT);
        }
        try {
            return supplier.get();
        }  finally {
            lock.unlock();
        }
    }

    public <T> T executeWithLock(String key, Supplier<T> supplier) {
        return executeWithLock(key, -1, TimeUnit.MILLISECONDS, supplier);
    }

    /**
     *
     * @param key
     * @param runnable 既不传参，也不返参
     * @param <T>
     * @return
     * @throws InterruptedException
     */
    public <T> T executeWithLock(String key, int waiteTime, TimeUnit timeUnit, Runnable runnable) {
        return executeWithLock(key, waiteTime, timeUnit, () -> {
            runnable.run();
            return null;
        });
    }

    @FunctionalInterface
    public interface Supplier<T> {

        /**
         * 切面的oinPoint::proceed需要抛出异常，而原本的Supplier是没有抛出异常的所以就定义一个并抛出异常
         */
        T get() throws Throwable;
    }
}
